﻿using Tesla.Bounding;
using Tesla.Core;
using Tesla.Graphics;
using Tesla.Math;
using Tesla.Scene.Shape;

namespace TeslaSamples.Renderer {
	[AppDescription("Materials System", PlatformCapabilities.Direct3D10 | PlatformCapabilities.XNA4, "MaterialsImage", "MaterialsDesc", "MaterialsSource")]
	public class MaterialsSample : BasicApp {
		protected override void LoadContent() {
			Window.Title = "Materials Sample";
			//Materials are a high level construct that are used to render objects. They contain the following:
			//
			//1. An effect shader that contains a set of techniques, where each techniques have a number of passes.
			//2. Render states for each pass.
			//3. Collection of uniform parameters.
			//4. Collection of engine parameters.
			//5. Material logic.
			//
			//The first three are simply an extension of using an Effect object directly (and for managing render states at the app level, opposed
			//to setting them in an effect file).
			// 
			//Engine parameters are simply a binding between an uniform variable and an engine-defined value such as a World-View-Projection matrix,
			//camera position, time variables, etc. This binding allows this data to be served to the shader automatically per-frame.
			//
			//Material logic are similar to Spatial controllers as they allow the user to add pieces of logic that are executed when the material is
			//applied. This allows for the concept of "Pre-Shaders", where you compute values on the CPU and send that data to the GPU. This is useful
			//for computations that only should be done once, rather than for every vertex or pixel fragment (saving instructions and time). An
			//example of logic usage is how lighting is implemented in the engine - it's nothing more than a material logic that operates on the collection
			//of lights a renderable contains.
			//
			//
			//There are two ways to create a material - programmatically or via the engine's scripting functionality. Material scripts allow for the user
			//to device a material in a text file that can be used as a template for other scripts and changed without having to write any C# code. 

			//Create a basic texture material 
			Material myMaterial = new Material("MyMaterial");
			myMaterial.LoadEffect(ContentManager.Load<Effect>("BasicEffect.tebo").Clone());
			myMaterial.SetActiveTechnique("BasicTexture");
			myMaterial.SetParameter("DiffuseMap", ContentManager.Load<Texture2D>("Textures//rock_diff.dds"));
			myMaterial.SetParameter("DiffuseColor", Color.White);
			myMaterial.SetEngineParameter("WVP", EngineValue.WorldViewProjection);

			/* The above would look like this as a script:
			 * 
			Material MyMaterial {
				Effect {
					File : BasicEffect.tebo
					Technique : BasicTexture
				}

				MaterialParameters {
					Vector4 DiffuseColor : 1.0 1.0 1.0 1.0
					Texture2D DiffuseMap : Textures/rock_diff.dds
				}

				EngineParameters {
					WorldViewProjection : WVP
				}
			}*/

			/* Additionally, you can use an existing script as a template. The BasicTexture.tem is
			 * a script already present in the engine's shader library and looks like the above script,
			 * minus the texture2d. So we use the script, and simply add a new parameter.
			 * 
			Material MyMaterial : BasicTexture.tem {
	
				MaterialParameters {
					Texture2D DiffuseMap : Textures/rock_diff.dds
				}
			}*/

			Material scriptMaterial = ContentManager.Load<Material>("Materials//SampleMaterial.tem").Clone();

			//To differentiate the two, lets set the scripted material's diffuse color to light blue
			scriptMaterial.SetParameter("DiffuseColor", Color.LightBlue);

			//Set our procedural material to the left.
			Box b1 = new Box("B1", 20, 20, 20);
			b1.Material = myMaterial;
			b1.Translation = new Vector3(-50, 0, 0);
			RootNode.AddChild(b1);

			//Set our scripted material to the right.
			Box b2 = new Box("B2", 20, 20, 20);
			b2.Material = scriptMaterial;
			b2.Translation = new Vector3(50, 0, 0);
			RootNode.AddChild(b2);

			RootNode.SetModelBound(new BoundingBox());
		}
	}
}
